package com.zmj.sy.mom.srv.aps.bean.vo.mbd.factory;

import cn.hutool.core.bean.BeanUtil;
import com.zmj.sy.mom.srv.aps.bean.entity.order.Order;
import com.zmj.sy.mom.srv.aps.bean.entity.order.OrderBom;
import com.zmj.sy.mom.srv.aps.bean.entity.order.OrderPhase;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.MbdBalanceRateDataPhaseResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.MbdBalanceRateDataResVo;
import com.zmj.sy.mom.srv.aps.utils.Comment;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Getter
@Setter
public class MbdBalanceRateOfFactoryResVo {

    @ApiModelProperty("前十工序")
    private GraphData top;

    @ApiModelProperty("所有工序")
    private GraphData all;

    @ApiModelProperty("关键工序")
    private GraphData critical;

    @ApiModelProperty("工位分配执行表")
    private StationAllocationData allocation;

    @ApiModelProperty("数量")
    private Integer orderQty;

    @Getter
    @Setter
    public static class GraphData {

        @ApiModelProperty("平衡率")
        private BigDecimal balanceRate;

        @ApiModelProperty("平衡损失率")
        private BigDecimal equilibriumLossRate;

        @ApiModelProperty("瓶颈工序工时")
        private BigDecimal bottleneckProcess;

        @ApiModelProperty("瓶颈工序阶段， P1、P2、P3、P4、P5")
        private String bottleneckProcessStage;

        @ApiModelProperty("瓶颈工序名称")
        private String bottleneckProcessName;

        @ApiModelProperty("总工时")
        private BigDecimal totalWorkingHours;

        @ApiModelProperty("总工位数")
        private BigDecimal totalStationCount;

        @ApiModelProperty("平衡指标")
        private List<GraphItem> graphData = new ArrayList<>();

    }

    @Getter
    @Setter
    public static class GraphItem {

        @ApiModelProperty("数据项名称")
        private String name;

        @ApiModelProperty("数据项值")
        private BigDecimal value;

        @Comment(value = "工序名称")
        private String phaseName;

        @Comment(value = "工序编号")
        private String phaseCode;

    }

    @Getter
    @Setter
    public static class StationAllocationData {

        @ApiModelProperty("工序列名")
        List<TableColumn> columns = new ArrayList<>();

        @ApiModelProperty("表格数据")
        List<TableRow> rows = new ArrayList<>();

        private void accumulate(StationAllocationData allocation) {
            this.getRows().addAll(allocation.getRows());
            this.setColumns(TableColumn.merge(this.getColumns(), allocation.getColumns()));
        }

        public void accumulate(Order order, Map<String, OrderBom> bomMap, Map<Integer, List<OrderPhase>> phaseMap, MbdBalanceRateDataResVo balanceRate) {
            // 架数：1200/最大节拍
            BigDecimal maxRhythm = balanceRate.getPhase().values().stream().flatMap(List::stream)
                    .filter(e -> !e.isHidden() && Arrays.asList("PH", "LH", "BH").contains(e.getPhaseCode()))
                    .map(MbdBalanceRateDataPhaseResVo::getProcessRhythm)
                    .reduce(BigDecimal.ZERO, BigDecimal::max)
                    .divide(BigDecimal.valueOf(order.getOrderQty()), 2, RoundingMode.HALF_UP);
            BigDecimal totalCount = new BigDecimal(1200).divide(maxRhythm, 2, RoundingMode.HALF_UP);
            // 单架总焊丝量：所有工序（包括人工）焊丝量总和
            BigDecimal totalWeldingWire = balanceRate.getPhase().values().stream().flatMap(List::stream)
                    .filter(e -> !e.isHidden() && Arrays.asList("PH", "LH", "BH", "RH").contains(e.getPhaseCode()) && e.getWeldingWireCount() != null)
                    .map(MbdBalanceRateDataPhaseResVo::getWeldingWireCount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // 覆盖率：机器焊丝总量（包括一次背、一次平、一次立）/机器和人工焊丝总量（包括一次背、一次平、一次立、人工）
            BigDecimal agentWeldingWire = balanceRate.getPhase().values().stream().flatMap(List::stream)
                    .filter(e -> !e.isHidden() && Arrays.asList("PH", "LH", "BH").contains(e.getPhaseCode()) && e.getWeldingWireCount() != null)
                    .map(MbdBalanceRateDataPhaseResVo::getWeldingWireCount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal coverage = totalWeldingWire.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO
                    : agentWeldingWire.multiply(BigDecimal.valueOf(100)).divide(totalWeldingWire, 2, RoundingMode.HALF_UP);
            // 平衡率：所有工序单架时间(工位数量*工序节拍)总和/(最大工序节拍*所有分配焊接站总数)
            BigDecimal totalTime = balanceRate.getPhase().values().stream().flatMap(List::stream)
                    .filter(e -> !e.isHidden() && Arrays.asList("PH", "LH", "BH").contains(e.getPhaseCode()))
                    .map(e -> e.getProcessRhythm().multiply(e.getStationCount()).divide(BigDecimal.valueOf(order.getOrderQty()), 2, RoundingMode.HALF_UP))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal totalStationCount = balanceRate.getPhase().values().stream().flatMap(List::stream)
                    .filter(e -> !e.isHidden() && Arrays.asList("PH", "LH", "BH").contains(e.getPhaseCode()))
                    .map(MbdBalanceRateDataPhaseResVo::getStationCount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal br = totalTime.multiply(BigDecimal.valueOf(100)).divide(maxRhythm.multiply(totalStationCount), 2, RoundingMode.HALF_UP);
            // 添加六行：工序、分配焊接站、节拍、单架时间、焊丝量（KG）、最大下线时间间隔
            String componentName = calculateComponentName(order);
            String[] itemNames = {"工序", "分配焊接站", "节拍", "单架时间", "焊丝量（KG）", "最大下线时间间隔"};
            StationAllocationData orderStationAllocation = new StationAllocationData();
            for (String itemName : itemNames) {
                TableRow tableRow = new TableRow();
                tableRow.setComponentLabel("部件");
                tableRow.setComponentName(componentName);
                tableRow.setItemName(itemName);
                tableRow.setTotalCount(totalCount);
                tableRow.setWeldingWireCount(totalWeldingWire);
                tableRow.setCoverRate(coverage);
                tableRow.setBalanceRate(br);
                orderStationAllocation.getRows().add(tableRow);
            }
            balanceRate.getPhase().forEach((stage, phases) -> phases.forEach(phase -> orderStationAllocation.accumulate(stage, phase, BigDecimal.valueOf(order.getOrderQty()))));
            accumulate(orderStationAllocation);
        }

        /**
         * 工序：一次背、一次立、一次平、二次背、二次立、二次平、三次背、三次立、三次平、四次背、四次立、四次平、五次背、五次立、五次平、六次背、六次立、六次平、PDB、人工
         * 分配焊接站：工位数量
         * 节拍：工序节拍
         * 单架时间：工位数量*工序节拍
         * 焊丝量（KG）：焊丝量（人工焊丝量=该结构件的所有人工焊焊丝量总和）
         * 最大下线时间间隔：24*60*分配焊接站数量/架数
         */
        private void accumulate(String stage, MbdBalanceRateDataPhaseResVo phase, BigDecimal count) {
            if (phase.isHidden()) {
                return;
            }
            // 计算工序字段名
            String fieldName = calculateFieldName(stage, phase.getPhaseCode());
            if (fieldName == null) {
                return;
            }
            // 计算字段值
            List<String> fieldValues = calculateFieldValues(fieldName, phase, count);
            // 数据装配
            for (int index = 0; index < rows.size(); index++) {
                if (fieldName.equals("rh") && index == 4) {
                    if (fieldValues.get(index) != null) {
                        if (rows.get(index).getRh() == null) {
                            rows.get(index).setRh(fieldValues.get(index));
                        } else {
                            rows.get(index).setRh(new BigDecimal(rows.get(index).getRh()).add(new BigDecimal(fieldValues.get(index))).toString());
                        }
                    }
                    continue;
                }
                BeanUtil.setProperty(rows.get(index), fieldName, fieldValues.get(index));
            }
            this.columns.add(TableColumn.of(fieldValues.get(0)));
        }

        private String calculateFieldName(String stage, String phaseCode) {
            // 人工
            if ("RH".equals(phaseCode)) {
                return "rh";
            }
            // PDB
            if ("PDB".equals(stage)) {
                if ("PH".equals(phaseCode)) {
                    return "pdb";
                }
                return null;
            }
            if (Arrays.asList("PH", "LH", "BH").contains(phaseCode)) {
                return phaseCode.toLowerCase().concat(stage.substring(1));
            }
            return null;
        }

        private List<String> calculateFieldValues(String fieldName, MbdBalanceRateDataPhaseResVo phase, BigDecimal count) {
            BigDecimal weldingWireCount = phase.getWeldingWireCount();
            if (weldingWireCount != null) {
                weldingWireCount = weldingWireCount.setScale(2, RoundingMode.HALF_UP);
            }
            if ("rh".equals(fieldName)) {
                return Arrays.asList("人工", null, null, null, Objects.toString(weldingWireCount, null), null);
            }
            BigDecimal processRhythm = phase.getProcessRhythm().divide(count, 2, RoundingMode.HALF_UP);
            return Arrays.asList(
                    calculatePhaseName(fieldName),
                    phase.getStationCount().toString(),
                    processRhythm.toString(),
                    phase.getStationCount().multiply(processRhythm).setScale(2, RoundingMode.HALF_UP).toString(),
                    Objects.toString(weldingWireCount, null),
                    BigDecimal.valueOf(24 * 60).multiply(phase.getStationCount()).divide(this.getRows().get(0).getTotalCount(), 2, RoundingMode.HALF_UP).toString()
            );
        }

        private static String calculatePhaseName(String fieldName) {
            String name = fieldName.replace("bh", "背")
                    .replace("lh", "立")
                    .replace("ph", "平")
                    .replace("rh", "人工")
                    .replace("pdb", "PDB");
            if (name.matches(".+\\d")) {
                int index = Integer.parseInt(name.substring(name.length() - 1));
                String suffix = name.substring(0, name.length() - 1);
                return Arrays.asList("一次", "二次", "三次", "四次", "五次", "六次").get(index - 1).concat(suffix);
            }
            return name;
        }

        private String calculateComponentName(Order order) {
            Pattern pattern = Pattern.compile("^.*(?<type>0[123]01)P.+$");
            Matcher matcher = pattern.matcher(order.getMaterCode());
            if (!matcher.find()) {
                return null;
            }
            String type = matcher.group("type");
            switch (type) {
                case "0101":
                    return "顶梁";
                case "0201":
                    return "掩护梁";
                case "0301":
                    return "底座";
                default:
                    return null;
            }
        }

    }

    @Getter
    @Setter
    public static class TableColumn {

        private String label;

        private String key;

        private static final List<TableColumn> columns = columnsMap();

        public static TableColumn of(String label) {
            return columns.stream().filter(column -> label.equals(column.getLabel())).findFirst().orElse(null);
        }

        private static List<TableColumn> columnsMap() {
            List<String> columns = Arrays.asList(
                    "一次背", "bh1",
                    "一次立", "lh1",
                    "一次平", "ph1",
                    "二次背", "bh2",
                    "二次立", "lh2",
                    "二次平", "ph2",
                    "三次背", "bh3",
                    "三次立", "lh3",
                    "三次平", "ph3",
                    "四次背", "bh4",
                    "四次立", "lh4",
                    "四次平", "ph4",
                    "五次背", "bh5",
                    "五次立", "lh5",
                    "五次平", "ph5",
                    "六次背", "bh6",
                    "六次立", "lh6",
                    "六次平", "ph6",
                    "PDB", "pdb",
                    "人工", "rh");
            List<TableColumn> result = new ArrayList<>();
            for (int i = 0; i < columns.size(); i += 2) {
                TableColumn column = new TableColumn();
                column.setKey(columns.get(i + 1));
                column.setLabel(columns.get(i));
                result.add(column);
            }
            return result;
        }

        public static List<TableColumn> merge(List<TableColumn> one, List<TableColumn> another) {
            return columns.stream().filter(column -> one.contains(column) || another.contains(column)).collect(Collectors.toList());
        }

    }

    @Getter
    @Setter
    public static class TableRow {

        @ApiModelProperty("部件")
        private String componentLabel;

        @ApiModelProperty("部件名称：顶梁、掩护梁、底座")
        private String componentName;

        @ApiModelProperty("项目名称：工序、分配焊接站、节拍、单架时间、焊丝量（KG）、最大下线时间间隔")
        private String itemName;

        @ApiModelProperty("一次背")
        private String bh1;

        @ApiModelProperty("一次立")
        private String lh1;

        @ApiModelProperty("一次平")
        private String ph1;

        @ApiModelProperty("二次背")
        private String bh2;

        @ApiModelProperty("二次立")
        private String lh2;

        @ApiModelProperty("二次平")
        private String ph2;

        @ApiModelProperty("三次背")
        private String bh3;

        @ApiModelProperty("三次立")
        private String lh3;

        @ApiModelProperty("三次平")
        private String ph3;

        @ApiModelProperty("四次背")
        private String bh4;

        @ApiModelProperty("四次立")
        private String lh4;

        @ApiModelProperty("四次平")
        private String ph4;

        @ApiModelProperty("五次背")
        private String bh5;

        @ApiModelProperty("五次立")
        private String lh5;

        @ApiModelProperty("五次平")
        private String ph5;

        @ApiModelProperty("六次背")
        private String bh6;

        @ApiModelProperty("六次立")
        private String lh6;

        @ApiModelProperty("六次平")
        private String ph6;

        @ApiModelProperty("PDB")
        private String pdb;

        @ApiModelProperty("人工")
        private String rh;

        @ApiModelProperty("架数")
        private BigDecimal totalCount;

        @ApiModelProperty("单架总焊丝量")
        private BigDecimal weldingWireCount;

        @ApiModelProperty("覆盖率")
        private BigDecimal coverRate;

        @ApiModelProperty("平衡率")
        private BigDecimal balanceRate;

        @ApiModelProperty("备注")
        private String remark;

    }

}
